/**
 * @高德地图常用方法封装
 * @参数key为高德申请好的开发者key值_此处需要替换为自己的
 *
 * @使用注意
 * 1、需要下载对应的地图npm包进行引入才能生效
 * 2、此方法必须要有个对应的key值，这个key需要在高德官网上申请，可参考此流程 https://lbs.amap.com/api/jsapi-v2/guide/abc/prepare
 * 3、因为此处为一个项目的使用，故我再次直接导出函数执行使用（偷懒），真正的操作应该是导出函数，使用时传入key
 * 4、此方法导出为一个 异步Promise属性，在使用的时候，需要在其.then中获取到进行操作的函数内容，或者直接使用 async 配合 await即可
 * 5、如果需要某些功能点必须要在当前地图展示时，每个函数的map参数是必须传递的，且这个参数应该是从 init 中回调处理的实例
 * 6、如果只是需要某些功能，如只是需要当前定位内容，并不用对地图进行改变，则map参数不用传递，但是后续参数按需使用
 * 7、跳转到高德自身地图页面进行导航可参考: https://lbs.amap.com/api/uri-api/guide/travel/route 官方文档进行对应使用
 *
 * @使用示例
 * 1、在对应的页面引入本文件：
 *    import cityFun from './js/cityFun'
 * 2、如果在是参考现在本函数的导出写法，即导出为 newAMap():
 *    cityFun.then(res=>{ 此res则是导出的函数对象合集 })
 * 3、如果为未执行导出，即导出为 newAMap：
 *    cityFun(携带开发者的key值).then(res=>{ 此res则是导出的函数对象合集 })
 */
// ---------------------------------------------------------------------------------------------
import AMapLoader from '@amap/amap-jsapi-loader' // 引入需要的地图内容

async function newAMap(key) {
  /**
   * 按照官方初始化方法进行使用
   * */
  // ---------------------------------------------------------------------------------------------
  let AMap = await AMapLoader.load({
    key, // 申请好的Web端开发者Key，首次调用 load 时必填
    version: '1.4.15', // 指定要加载的 JSAPI 的版本，缺省时默认为 1.4.15
    // 是否加载 AMapUI，缺省不加载
    AMapUI: {
      version: '1.1', // AMapUI 缺省 1.1
      plugins: [], // 需要加载的 AMapUI ui插件
    },
  })

  /**
   * @地图初始化函数
   * @map为声明的地图对象变量_需要一个变量值去承接_vue2中不能存在已经声明好的变量
   * @cityId为此地图需要的id
   * @center为可选量需要时传入初始化的目标经纬度
   * */
  // ---------------------------------------------------------------------------------------------
  function init(map, cityId, center) {
    map = new AMap.Map(cityId, {
      //设置地图容器id
      viewMode: '3D', //是否为3D地图模式
      mapStyle: 'amap://styles/whitesmoke', //设置地图的显示样式（图层颜色）
      zoom: 15, //初始化地图级别
      resizeEnable: true, // 是否对地图尺寸发生变化后进行监听
      center,
    })

    // 弹出注册好的map实例对象
    return map
  }

  /**
   * @地图定位方法
   * @map为声明的地图实例变量_不传则只是为了得到定位信息
   * @onComplete执行成功后需要调用函数_携带地址信息_必传
   * @onError执行失败后需要调用的函数_携带错误信息_必传
   * @需要自定义的内容对象_可选参数
   * */
  // ---------------------------------------------------------------------------------------------
  function cityLocation(map, onComplete, onError, needObj = {}) {
    // 从自定义内容对象中进行结构
    const {
      enableHighAccuracy = true, //是否使用高精度定位，默认:true
      timeout = 10000, //超过10秒后停止定位，默认：10000
      maximumAge = 0, //定位结果缓存0毫秒，默认：0
      convert = true, //自动偏移坐标，偏移后的坐标为高德坐标，默认：true
      showButton = true, //显示定位按钮，默认：true
      buttonPosition = 'RB', //定位按钮停靠位置，默认：'RB'，左下角
      buttonOffset = new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量，默认：Pixel(10, 20)
      showMarker = false, //定位成功后在定位到的位置显示点标记，默认：false
      showCircle = false, //定位成功后用圆圈表示定位精度范围，默认：false
      panToLocation = true, //定位成功后将定位到的位置作为地图中心点，默认：true
      zoomToAccuracy = true, //定位成功后调整地图视野范围使定位位置及精度范围视野内可见，默认：false
    } = needObj

    // 进行定位组件的自我功能封装
    AMap.plugin('AMap.Geolocation', function () {
      let geolocation = new AMap.Geolocation({
        enableHighAccuracy,
        timeout,
        maximumAge,
        convert,
        showButton,
        buttonPosition,
        buttonOffset,
        showMarker,
        showCircle,
        panToLocation,
        zoomToAccuracy,
      })

      // 给地图绑定我们自定义的地图组件
      map && map.addControl(geolocation)

      // 进行添加监听方法
      geolocation.getCurrentPosition(function (status, result) {
        if (status == 'complete') {
          // 如果定位成功后的回调函数
          onComplete(result)
        } else {
          // 定位失败后的回调函数
          onError(result)
        }
      })
    })
  }

  /**
   * @地图拖拽函数
   * @map为声明的地图实例变量
   * @success为拖拽结束时的回调函数_携带地址信息_必传
   * @fail为拖拽失败的回调函数_携带地址信息_必传
   *  */
  // ---------------------------------------------------------------------------------------------
  function cityDrag(map, success, fail, icon = {}) {
    /** 从icon参数中结构出需要图标修改信息 */
    let {
      url = 'https://img.yzcdn.cn/public_files/2020/03/04/32a548551986a2c3c22ef3018eb7a9af.png', //图片地址
      size = [22, 36], //要显示的点大小，将缩放图片
      ancher = [11, 40], //锚点的位置，即被size缩放之后，图片的什么位置作为选中的位置
    } = icon

    // 使用地图拖拽方法
    AMapUI.loadUI(['misc/PositionPicker'], function (PositionPicker) {
      // 进行使用拖拽组件的自定义方法
      let positionPickers = new PositionPicker({
        mode: 'dragMap',
        map: map,
        //自定义中心图标外观
        iconStyle: {
          url,
          size,
          ancher,
        },
      })
      // 定位成功后的回调方法，会携带定位信息
      positionPickers.on('success', success)
      // 定位失败后的回调方法，会携带错误原因
      positionPickers.on('fail', fail)
      // 开启拖拽模式
      positionPickers.start()
    })
  }

  /**
   * @地图条件搜索方法_无对应坐标信息
   * @value需要查询的字符串
   * @searchResult查询结果执行的回调函数_携带查询后的信息_必传
   * @needCity可选参数为限制搜索的城市范围
   */
  // ---------------------------------------------------------------------------------------------
  function citySearch(value, searchResult, needCity) {
    AMap.plugin('AMap.AutoComplete', () => {
      let autoOptions = {
        city: needCity || '全国', //needCity 限定城市，默认全国
        pageSize: 20, // 单页显示结果条数
        // children: 0, //不展示子节点数据
        pageIndex: 1, //页码
        // extensions: 'base', //返回基本地址信息
      }

      // 实例化AutoComplete
      let autoComplete = new AMap.AutoComplete(autoOptions)

      // 根据关键字进行搜索
      autoComplete.search(value, (status, result) => {
        // 执行查询回调函数，将查询后的结果进行返回
        searchResult(result)
      })
    })
  }

  /**
   * @地图条件搜索方法_有对应坐标信息
   * @value需要查询的字符串
   * @searchResult查询结果执行的回调函数_携带查询后的信息_必传
   * @needCity可选参数为限制搜索的城市范围
   */
  // ---------------------------------------------------------------------------------------------
  function cityPlaceSearch(value, searchResult, needCity) {
    AMap.plugin('AMap.PlaceSearch', () => {
      let autoOptions = {
        city: needCity || '全国', //needCity 限定城市，默认全国
        pageSize: 20, // 单页显示结果条数
        pageIndex: 1, // 页码
        citylimit: true, //是否强制限制在设置的城市内搜索
      }

      // 实例化AutoComplete
      let placeSearch = new AMap.PlaceSearch(autoOptions)

      // 根据关键字进行搜索
      placeSearch.search(value, (status, result) => {
        // 执行查询回调函数，将查询后的结果进行返回
        searchResult(result)
      })
    })
  }

  /**
   * @改变地图中心点方法
   * @map为声明的地图实例变量
   * @position坐标系数组_必传
   */
  // ---------------------------------------------------------------------------------------------
  function citySetCenter(map, position) {
    // 运用高德地图使用方法，进行改变本地图的中心展示点
    map.setCenter(position)
  }

  /**
   * @地图导航描点方法
   * @map为声明的地图实例变量
   * @position坐标系数组_必传
   * @icons图标展示对象_有image的图片地址就可以展示icon不然则是正常情况
   * @markerClick点击回调函数_回调此点的实例_必传
   * @content提示导航的内容块
   * @instanceThrow中途抛出定义好的标识实例用于其他点击函数操作
   */
  // ---------------------------------------------------------------------------------------------
  function cityNavigation(
    map,
    position,
    icons = {},
    markerClick,
    content,
    instanceThrow
  ) {
    const {
      size = new AMap.Size(20, 27), // 图标尺寸
      image, // Icon的图像
      imageOffset = new AMap.Pixel(0, -60), // 图像相对展示区域的偏移量，适于雪碧图等
      imageSize = new AMap.Size(20, 27), // 根据所设置的大小拉伸或压缩图片
    } = icons

    // icon实例
    let icon = new AMap.Icon({
      size,
      image,
      imageOffset,
      imageSize,
    })

    // 创建 AMap.Icon 实例：
    AMap.plugin('AMap.Marker', () => {
      // 地图标识初始化对象，如果有image图片地址则使用icon标签形式，否则是不带icon
      let markerObj = image ? { map, position, icon } : { map, position }

      // 声明使用地图打标方法
      let marker = new AMap.Marker(markerObj)

      // 改变标点样式
      marker.setLabel({
        offset: new AMap.Pixel(-70, -60), //修改label相对于maker的位置
        content, // 点标记显示内容，可以是HTML要素字符串或者HTML DOM对象。content有效时，icon属性将被覆盖
      })

      instanceThrow && instanceThrow(marker)

      // 此标记点点击时触发的回调函数，回调此坐标点的实例
      marker.on('click', function (e) {
        markerClick(marker)
      })
    })

    // 做一个参考的方法内容
    // marker.on('click', function (e) {
    // // 此方法为点击后唤唤起高德地图客户端标注页，其中Object里面包含有{ name:String, 必要参数 position: LngLat 坐标点}
    //   marker.markOnAMAP({
    //     position: marker.getPosition(),
    //   })
    // })
  }

  /**
   * @根据经纬度查询地址信息方法
   * @position当前经纬度数组_必传
   * @obtainAddress查询成功后的回调函数_带查询地址信息_必传
   */
  // ---------------------------------------------------------------------------------------------
  function longitudeAndLatitudeToAddress(position, obtainAddress) {
    AMap.plugin('AMap.Geocoder', function () {
      var geocoder = new AMap.Geocoder({
        // city 指定进行编码查询的城市，支持传入城市名、adcode 和 citycode
        city: '全国',
      })

      // 查询地址方法
      geocoder.getAddress(position, function (status, result) {
        if (status === 'complete' && result.info === 'OK') {
          // result为对应的地理位置详细信息
          obtainAddress(result)
        }
      })
    })
  }

  /**
   * @根据地址查询经纬度方法
   * @addressName当前查询地址的字符串
   * @obtainLongitudeAndLatitude查询成功后的回调函数_带查询地址的经纬度_必传
   */
  // ---------------------------------------------------------------------------------------------
  function addressToLongitudeAndLatitude(
    addressName,
    obtainLongitudeAndLatitude
  ) {
    AMap.plugin('AMap.Geocoder', function () {
      var geocoder = new AMap.Geocoder({
        // city 指定进行编码查询的城市，支持传入城市名、adcode 和 citycode
        city: '全国',
      })

      // 查询经纬度方法
      geocoder.getLocation(addressName, function (status, result) {
        if (status === 'complete' && result.info === 'OK') {
          // result中对应详细地理坐标信息
          obtainLongitudeAndLatitude(result)
        }
      })
    })
  }

  /**
   * @给地址添加事件函数
   * @map必填_为地图实例变量
   * @eventName事件类型字段_如click
   * @effectiveFunction触发生效函数
   * 触发生效的函数会携带的参数可以参考此文章：https://lbs.amap.com/api/jsapi-v2/guide/events/map_overlay
   */
  // ---------------------------------------------------------------------------------------------
  function cityMapAddEvent(map, eventName, effectiveFunction) {
    map.on(eventName, effectiveFunction)
  }

  /**
   * @动态计算两个坐标地点的实际距离
   * @starList_起始地点坐标数组
   * @endList_结束地点的坐标数组
   * 其他计算方法可以参考此文章：https://lbs.amap.com/api/javascript-api/guide/geometry/geometry
   */
  // ---------------------------------------------------------------------------------------------
  function twoPointDistance(starList, endList) {
    // 使用动态计算两点坐标距离的方法
    let dis = AMap.GeometryUtil.distance(starList, endList)

    // 弹出计算结果
    return dis
  }

  /**
   * @其他坐标转高德坐标
   * @conversionList_需要转化的地点坐标数组
   * @type用于说明是哪个服务商的坐标
   * type可选值有：gps:GPS原始坐标；baidu：百度经纬度；mapbar：图吧经纬度
   * @successFun转化成功后的回调函数_传入修改成功后的坐标数据
   * 其他计算方法可以参考此文章：https://lbs.amap.com/api/javascript-api/guide/geometry/geometry
   */
  // ---------------------------------------------------------------------------------------------
  function cityCoordinateConversion(conversionList, type, successFun) {
    // 使用高德自带转化坐标系方法
    AMap.convertFrom(conversionList, type, function (status, result) {
      if (result.info === 'ok') {
        // 转化成功后调用的回调函数，并把对应的转化坐标传入
        successFun(result.locations)
      }
    })
  }

  // 弹出目前封装好的方法
  // ---------------------------------------------------------------------------------------------
  return {
    init, // 地图初始化函数
    cityLocation, // 地图定位方法
    cityDrag, // 地图拖拽函数
    citySearch, // 地图条件搜索方法_无对应坐标信息
    cityPlaceSearch, // 地图条件搜索方法_有对应坐标信息
    citySetCenter, // 改变地图中心点方法
    cityNavigation, // 地图导航描点方法
    longitudeAndLatitudeToAddress, // 根据经纬度查询地址信息方法
    addressToLongitudeAndLatitude, // 根据地址查询经纬度方法
    cityMapAddEvent, // 给地址添加事件函数
    twoPointDistance, // 动态计算两个坐标地点的实际距离
    cityCoordinateConversion, // 其他坐标转高德坐标
  }
}

// 导出此封装好的方法函数
export default newAMap
